package com.zee.admin.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zee.admin.dao.SysUserTokenDao;
import com.zee.admin.entity.SysUserToken;
import com.zee.admin.service.ISysUserTokenService;
import com.zee.admin.shiro.AdminTokenGenerator;
import com.zee.common.constant.Constant;
import com.zee.common.redis.RedisKeys;
import com.zee.common.redis.RedisUtil;
import com.zee.common.utils.TimeConvertUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

@Service
public class SysUserTokenServiceImpl extends ServiceImpl<SysUserTokenDao, SysUserToken> implements ISysUserTokenService {
    @Value("${zee.redis.open: false}")
    private boolean isOpenRedis;

    @Value("${zee.token.expireTime: 86400}")
    private Integer expireTime;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    public Map<String, Object> createToken(Integer userId) {
        Map<String, Object> map;
        if (isOpenRedis) {
            map = this.saveRedisToken(userId);
        } else {
            map = this.saveDbToken(userId);
        }

        return map;
    }

    /**
     * 向数据库存token
     *
     * @return
     */
    private Map<String, Object> saveDbToken(Integer userId) {
        LocalDateTime now = LocalDateTime.now();
        //用户token
        String token;
        //判断是否生成过token
        SysUserToken sysUserToken = baseMapper.getByUserId(userId);
        if (sysUserToken == null) {
            //生成一个token
            token = AdminTokenGenerator.generateValue();

            sysUserToken = new SysUserToken();
            sysUserToken.setUserId(userId);
            sysUserToken.setToken(token);
            sysUserToken.setExpireTime(now.plusSeconds(this.expireTime));

            //保存token
            this.save(sysUserToken);
        } else {
            //判断token是否过期
            if (TimeConvertUtil.getMillisTime(sysUserToken.getExpireTime()) < System.currentTimeMillis()) {
                //token过期，重新生成token
                token = AdminTokenGenerator.generateValue();
            } else {
                token = sysUserToken.getToken();
            }

            sysUserToken.setToken(token);
            sysUserToken.setExpireTime(now.plusSeconds(this.expireTime));

            //更新token
            this.updateById(sysUserToken);
        }

        Map<String, Object> map = new HashMap<>(2);
        map.put(Constant.TOKEN_HEADER, sysUserToken.getToken());
        map.put("expire", this.expireTime);
        map.put("createTime", sysUserToken.getCreateTime());
        map.put("expireTime", sysUserToken.getExpireTime());
        return map;
    }

    /**
     * 向redis存token
     *
     * @return
     */
    private Map<String, Object> saveRedisToken(Integer userId) {
        String token = (String) redisUtil.get(RedisKeys.getAdminUserKey(userId));
        // 普通token串需要存2个key方便转换，
        // 如果是jwt，则存userid的key就可以，因为可以从jwt中解析出userId，再根据userId查找Key
        if (token == null) {
            token = AdminTokenGenerator.generateValue();
            this.setRedisToken(token, userId);
        }

        String info = (String) redisUtil.get(RedisKeys.getAdminUserToken(token));
        SysUserToken sysUserToken = JSONObject.parseObject(info, SysUserToken.class);
        Map<String, Object> map = new HashMap<>(2);
        if (sysUserToken != null) {
            map.put(Constant.TOKEN_HEADER, sysUserToken.getToken());
            map.put("expire", this.expireTime);
            map.put("createTime", sysUserToken.getCreateTime());
            map.put("expireTime", sysUserToken.getExpireTime());
        }
        return map;
    }

    /**
     * 普通token串需要保存token和userId的key
     *
     * @param token
     * @param userId
     */
    private void setRedisToken(String token, Integer userId) {
        //删除原来存token的key
        String redisToken = (String) redisUtil.get(RedisKeys.getAdminUserKey(userId));
        if (redisToken != null) {
            redisUtil.del(RedisKeys.getAdminUserToken(redisToken), RedisKeys.getAdminUserKey(userId));
        }

        LocalDateTime now = LocalDateTime.now();
        SysUserToken sysUserToken = new SysUserToken();
        sysUserToken.setToken(token);
        sysUserToken.setUserId(userId);
        sysUserToken.setExpireTime(now.plusSeconds(this.expireTime));
        sysUserToken.setCreateTime(now);
        sysUserToken.setUpdateTime(now);

        // 存token的Key，后续接口全是携带token串来查找userId的
        redisUtil.set(RedisKeys.getAdminUserToken(token), JSONObject.toJSONString(sysUserToken), this.expireTime);
        // 覆盖原来的token值（可以顶掉同一帐号登录的人），生成token时可以根据userid的key查找
        redisUtil.set(RedisKeys.getAdminUserKey(userId), token, this.expireTime);
    }

    @Override
    public void logout(Integer userId) {
        //生成一个新的token入库
        String token = AdminTokenGenerator.generateValue();
        if (this.isOpenRedis) {
            this.setRedisToken(token, userId);
        } else {
            //修改token，每个用户只生成一条记录，token没过期，下次登录返回，过期则重新生成
            baseMapper.updateToken(userId, token);
        }
    }
}